function dat = synth_notes(varargin)
% function dat = synth_notes(varargin)
%
% Synthesize a set of spectrograms for a set of pitches and a set of
% instruments.
%
% Input (all are passed in name-value pairs):
%   synth    [str]  - Software synthesizer to use.  ['timidity']
%                     Can be:
%                        'timidity' - Use the timidity synthesizer
%                        'flsynth'  - Use the Fluidsynth synthesizer
%   dsr      [num]  - Desired sampling rate [8000]
%   mono     [bool] - Render mono? [1]
%   tempo    [num]  - Tempo (in BPM) to render MIDI file at.  Empty means  
%                     leave the note matrix alone []
%   cfg      [str]  - Timidity config file to use ['']
%   trim_end [num]  - If greater than 0, the rendered output will be
%                     'trimmed' so that excess silence is removed. The
%                     actual value of trim_end is used as a threshold.  
%                     If trim is 0, the spectrogram length will be forced to 
%                     match dur by either trimming or adding noise, if 
%                     negative, nothing will be done [0]
%   decay    [num]  - If trim_end is 0, decay number of seconds will be
%                     included to account for decay [0]
%   delay    [num]  - Delay (in seconds) before each note starts [0]
%   insts    [vec]  - General MIDI numbers to use for each channel [0]
%   pitches  [vec]  - MIDI numbers of the pitches to render [60:72]
%   ttype    [str]  - Transform type to apply to synthesized waveform. Can
%                     be: 'spec', 'stft', or 'logf' (see st_trans.m) ['spec']
%   nfft     [num]  - FFT size to use for each frame [512]
%   win      [num]  - Length (in seconds) of each window [0.064]
%   hop      [num]  - Length (in seconds) of the overlap of each frame [0.016]
%   bpo      [num]  - Bands per octave (used by logf transform) [12]
%   min_freq [num]  - Minimum frequency to keep for Constant-Q [40]
%   dur      [num]  - Duration (in seconds) of each rendered note  [1]
%   vel      [vec]  - Velocity of each rendered note.  If a vector if given, 
%                     the the average of the spectra produced is used [80]
%   verb     [num]  - Verbosity level (0-3) [0]
%   mag_only [bool] - Return only magnitude? [1]
%
% Output:
%   dat      [cell] - Cell array of size (num instruments x num pitches)
%                     containing note spectrograms
% 

% Copyright (C) 2010-2030 Graham Grindlay (grindlay@ee.columbia.edu)
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.

% process arguments
[synth, dsr, mono, tempo, cfg, trim_end, decay, delay, insts, pitches, ttype, ...
 nfft, win, hop, bpo, min_freq, dur, vels, verb, mag_only] = ...
    parse_opt(varargin, 'synth', 'timidity', 'dsr', 8000, 'mono', 1, ...
                        'tempo', [], 'cfg', '', 'trim_end', 0, 'decay', 0, ...
                        'delay', 0, 'insts', 0, 'pitches', 60:72, ...
                        'ttype', 'spec', 'nfft', 512, 'win', 0.064, ...
                        'hop', 0.016, 'bpo', 12, 'min_freq', 40, 'dur', 1, ...
                        'vel', 80, 'verb', 0, 'mag_only', 1);
                    
ni = length(insts);
np = length(pitches);

dat = cell(ni,np);

for i = 1:ni
    if verb >= 1
        disp(['Instrument ' num2str(i) '/' num2str(ni)]);
    end
    for p = 1:np
        if verb >= 2
            disp(['Pitch ' num2str(p) '/' num2str(np)]);
        end
        
        tmp = 0;
        for v = 1:length(vels)
            % create note for this pitch and render it using the current instrument
            nmat = createnmat(pitches(p), dur, vels(v), 1, tempo);
            nmat(1) = tempo/60 * delay;
            nmat(6) = delay;
            [d,fs] = synth_nmat(nmat, 'insts', insts(i), 'tempo', tempo, ...
                                'cfg', cfg, 'dsr', 44100, 'mono', mono, ...
                                'trim_end', trim_end, 'decay', decay, ...
                                'synth', synth);
            
            % resample if necessary
            if dsr ~= fs
                d = resample(d, dsr, fs);
            end
            
            % if trim_end is 0 and d is shorter than dur, fill in the rest with noise
            if trim_end == 0
                if length(d) < floor((dur+decay)*dsr)
                    d = [d ; 0.0001*randn(floor((delay+dur+decay)*dsr)-length(d),1)];
                else
                    len = floor((delay+dur+decay)*dsr);
                    if length(d) < len
                        warning('requested note length is smaller than actual duration');
                    else
                        d = d(1:len);
                    end
                end
            end
            
            % compute the STFT
            tmp = tmp + st_trans(d, ttype, 'dsr', dsr, 'nfft', nfft, ...
                                 'win', win, 'hop', hop, 'bpo', bpo, ...
                                 'min_freq', min_freq);
        end
        dat{i,p} = tmp ./ length(vels);
        
        % only keep the magnitude if asked
        if mag_only
            dat{i,p} = abs(dat{i,p});
        end
    end
end
